
import java.util.List;
import java.util.Arrays;
/**
 *
 */
public class Board
{
    public final static int MAX_FITNESS = 1000;
    
    public final int height;
    public final int width;
    public static double dynamicFitnessMax;
    
    //From left to right, total number of ship cells that should be in each column
    public List<Integer> columnTargetList;
    
  //From top to bottom, total number of ship cells that should be in each row
    public List<Integer> rowTargetList;
    
    Board(int height, int width, List<Integer> columnTargetList, List<Integer> rowTargetList)
    {
        this.height = height;
        this.width = width;
        this.columnTargetList = columnTargetList;
        this.rowTargetList = rowTargetList;
        
        if (columnTargetList.size() != this.width) {
        	System.out.println("ERROR: column target list size does match board width");
        }
        
        if (rowTargetList.size() != this.width) {
        	System.out.println("ERROR: row target list size does match board height");
        }
    }

    //Calculates the fitness of the current board at the current level of the dynamic fitness schedule 
    public double getDynamicFitness(Ship[] ships, int level)
    {   
    	BoardResult boardResult = new BoardResult(ships, this, level);
    	double fitness = getFit(boardResult);
    	           
        dynamicFitnessMax = (1.0/ (1.0 + Math.max(0,((height+width)-level*2)))) * MAX_FITNESS;
        
        return fitness;
    }
    
    //Calculates the fitness of the current board with ships added
    public double getRawFitness(Ship[] ships)
    {   	
    	BoardResult boardResult = new BoardResult(ships, this, Integer.MAX_VALUE);    
    	double fitness = getFit(boardResult);
    	
    	return fitness;
    }
    
    private double getFit(BoardResult boardResult)
    {
    	double fitness = 0.0;
    	
    	fitness += boardResult.failingColumns;//.shipPartsFailingColumns;
    	fitness += boardResult.failingRows;//.shipPartsFailingRows;
    	fitness += boardResult.shipPartsOutOfBounds;
    	fitness += boardResult.shipPartsOverlapping;
        fitness += boardResult.shipPartsAdjacent;
    	
        //Invert results for maximization
    	//Add 1 to avoid dividing by 0
    	//Scale to 1000
        fitness = (1.0 / (fitness + 1.0)) * MAX_FITNESS;
        
        return fitness;
    }
    
    //Calculates the delta as defined in the WoaC Battleship paper
    public int getDelta(Ship[] ships)
    {   	
    	int delta = 0;
    	
    	BoardResult boardResult = new BoardResult(ships, this, Integer.MAX_VALUE);    
        
    	delta += boardResult.shipPartsFailingColumns;
    	delta += boardResult.shipPartsFailingRows;
    	delta += boardResult.shipsAdjacent;
        
        return delta;
    }
    
    //Determines if the ship is within the bounds of the board
    public boolean isShipWithinBounds(Ship s)
    {
        //Make sure coordinate lies within board range
        if (s.locationX > this.width || s.locationX < 0
                || s.locationY > this.height || s.locationY < 0)
            return false;
        
        //Make sure length/orientation are in bounds
        if (s.orientation == Ship.ORIENTATION_LEFT)
        {
            if (s.locationX - s.getLength() < 0)
                return false;
        }
        else if (s.orientation == Ship.ORIENTATION_RIGHT)
        {
            if (s.locationX + s.getLength() > this.width)
                return false;
        }
        
        //Make sure length/orientation are in bounds
        if (s.orientation == Ship.ORIENTATION_UP)
        {
            if (s.locationY - s.getLength() < 0)
                return false;
        }
        else if (s.orientation == Ship.ORIENTATION_DOWN)
        {
            if (s.locationY + s.getLength() > this.height)
                return false;
        }
        
        return true;
    }
}
